# 导入所需的系统和基础库
import os  # 用于文件和目录操作
import time  # 用于时间相关操作
import threading  # 用于多线程处理
import torch
import sys

# 补丁代码：替换tqdm函数，避免GUI环境中的进度条错误
import sys
import types
import functools

# 检查tqdm是否已导入
if 'tqdm' in sys.modules:
    # 创建一个无操作的tqdm替代函数
    def dummy_tqdm(*args, **kwargs):
        return args[0] if args else kwargs.get('iterable', None)
    
    # 替换tqdm.tqdm函数
    import tqdm
    tqdm.tqdm = dummy_tqdm
    
    # 替换tqdm.std模块中的tqdm
    if hasattr(tqdm, 'std') and hasattr(tqdm.std, 'tqdm'):
        tqdm.std.tqdm = dummy_tqdm

# 导入PyQt5相关的GUI组件
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, 
                            QWidget, QFileDialog, QListWidget, QTextEdit, QProgressBar, QLabel,
                            QFrame, QDesktopWidget, QMessageBox)
from PyQt5.QtCore import pyqtSignal, QObject, Qt, QTimer  # 导入Qt信号和槽机制相关的类
from PyQt5.QtGui import QFontDatabase, QFont  # 导入字体相关的类

# 导入 FunASR 语音识别相关模块
from funasr import AutoModel  # 导入自动模型类
from funasr.utils.postprocess_utils import rich_transcription_postprocess  # 导入后处理工具

# 信号类，用于在线程间通信
class SignalEmitter(QObject):
    # 定义各种信号
    progress_signal = pyqtSignal(int)  # 进度信号，用于更新进度条
    result_signal = pyqtSignal(str, str)  # 结果信号，用于显示转换结果
    error_signal = pyqtSignal(str)  # 错误信号，用于显示错误信息
    info_signal = pyqtSignal(str)  # 信息信号，用于显示提示信息
    finished_signal = pyqtSignal()  # 完成信号，用于通知转换完成

# 启动画面类
class SplashScreen(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowFlag(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setFixedSize(500, 500)
        
        # 居中显示
        screen_geometry = QApplication.desktop().screenGeometry()
        x = (screen_geometry.width() - self.width()) // 2
        y = (screen_geometry.height() - self.height()) // 2
        self.move(x, y)
        
        self.counter = 0
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_progress)
        self.timer.start(30)  # 30ms更新一次
        
        self.initUI()
        
    def initUI(self):
        layout = QVBoxLayout()
        self.setLayout(layout)
        
        # 创建圆形容器
        self.container = QFrame()
        container_layout = QVBoxLayout()
        self.container.setLayout(container_layout)
        self.container.setObjectName("splash_container")
        self.container.setStyleSheet("""
            #splash_container {
                background-color: #0F0F1A;
                border-radius: 250px;
                border: 2px solid #323264;
            }
        """)
        
        # 标题
        title = QLabel("SenseVoice AI")
        title.setAlignment(Qt.AlignCenter)
        title.setStyleSheet("""
            color: #00FFC8;
            font-size: 30px;
            font-weight: bold;
            padding: 10px;
        """)
        
        # 子标题
        subtitle = QLabel("语音转文字工具")
        subtitle.setAlignment(Qt.AlignCenter)
        subtitle.setStyleSheet("""
            color: #8D8DFF;
            font-size: 18px;
            padding: 10px;
        """)
        
        # 进度条
        self.progress = QProgressBar()
        self.progress.setMaximum(100)
        self.progress.setTextVisible(False)
        self.progress.setStyleSheet("""
            QProgressBar {
                border: none;
                border-radius: 15px;
                background-color: #191932;
                height: 30px;
                margin: 20px 50px;
            }
            QProgressBar::chunk {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #00DBFF, stop:0.5 #5D49F2, stop:1 #00FFC8);
                border-radius: 15px;
            }
        """)
        
        # 加载文本
        self.loading_text = QLabel("正在加载模型...")
        self.loading_text.setAlignment(Qt.AlignCenter)
        self.loading_text.setStyleSheet("""
            color: #D1D1FF;
            font-size: 16px;
            padding: 10px;
        """)
        
        # 创建AI科技感的图标
        self.icon = QLabel()
        self.icon.setAlignment(Qt.AlignCenter)
        # 使用HTML绘制一个SVG风格的图标
        self.icon.setText("""
            <div style="text-align: center; margin: 20px;">
                <svg width="120" height="120" viewBox="0 0 120 120">
                    <circle cx="60" cy="60" r="50" fill="none" stroke="#00FFC8" stroke-width="2" />
                    <circle cx="60" cy="60" r="40" fill="none" stroke="#5D49F2" stroke-width="2" />
                    <circle cx="60" cy="60" r="30" fill="none" stroke="#00DBFF" stroke-width="2" />
                    <circle cx="60" cy="60" r="20" fill="none" stroke="#7F63FF" stroke-width="2" />
                    <circle cx="60" cy="60" r="10" fill="#00FFC8" />
                </svg>
            </div>
        """)
        
        # 给布局添加控件
        container_layout.addWidget(self.icon)
        container_layout.addWidget(title)
        container_layout.addWidget(subtitle)
        container_layout.addWidget(self.progress)
        container_layout.addWidget(self.loading_text)
        
        # 添加感谢信息
        thanks = QLabel("感谢 SenseVoice 开源项目提供技术支持")
        thanks.setAlignment(Qt.AlignCenter)
        thanks.setStyleSheet("""
            color: #00DBFF;
            font-size: 12px;
            font-style: italic;
            padding-bottom: 5px;
            text-shadow: 0 0 5px #00DBFF;
        """)
        container_layout.addWidget(thanks)
        
        # 添加版权信息
        copyright = QLabel("© 2025 AI粉嫩特攻队")
        copyright.setAlignment(Qt.AlignCenter)
        copyright.setStyleSheet("""
            color: #FF7DFF;
            font-size: 14px;
            font-weight: bold;
            padding-bottom: 15px;
            text-shadow: 0 0 5px #FF7DFF;
        """)
        container_layout.addWidget(copyright)
        
        container_layout.addStretch()
        
        # 添加到主布局
        layout.addWidget(self.container)
    
    def update_progress(self):
        loading_texts = [
            "正在初始化...",
            "加载语音识别模型...",
            "准备语音引擎...",
            "配置用户界面...",
            "即将启动应用..."
        ]
        
        self.counter += 1
        self.progress.setValue(self.counter)
        
        # 更新加载文本
        text_index = min(self.counter // 20, len(loading_texts) - 1)
        self.loading_text.setText(loading_texts[text_index])
        
        # 达到100%时关闭启动画面
        if self.counter >= 100:
            self.timer.stop()
            self.close()
            
            # 显示主窗口
            self.window = SenseVoiceApp()
            self.window.show()

# 主应用程序类
class SenseVoiceApp(QMainWindow):
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()
    
    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self._drag_position is not None:
            self.move(event.globalPos() - self._drag_position)
            event.accept()
    
    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._drag_position = None
            event.accept()
    
    def __init__(self):
        super().__init__()
        
        icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ai.ico")
        if os.path.exists(icon_path):
            from PyQt5.QtGui import QIcon
            self.setWindowIcon(QIcon(icon_path))
        

        # 加载QSS样式表
        style_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'style.qss')
        if os.path.exists(style_file):
            with open(style_file, 'r', encoding='utf-8') as f:
                self.setStyleSheet(f.read())
        
        # 设置窗口无边框和保持在最前
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        
        # 添加窗口拖动支持
        self._drag_position = None
        
        # 获取运行时的基础路径
        if getattr(sys, 'frozen', False):
            # 如果是打包后的 exe，使用PyInstaller的临时目录
            self.base_path = sys._MEIPASS if hasattr(sys, '_MEIPASS') else os.path.dirname(sys.executable)
            self.exe_dir = os.path.dirname(sys.executable)  # 用于保存输出文件
        else:
            # 如果是开发环境
            # self.base_path = f"{os.path.dirname(sys.executable)}/_internal"  # 用于访问打包的资源文件
            self.base_path = os.path.dirname(os.path.abspath(__file__))
            # self.exe_dir = self.base_path
            self.exe_dir = os.path.dirname(sys.executable)  # 用于保存输出文件

        # 确保输出目录存在（改为在可执行文件目录下创建）
        self.output_dir = os.path.join(self.exe_dir, "mydata", "output")
        os.makedirs(self.output_dir, exist_ok=True)
        
        # 设置模型路径（改为相对路径）
        self.model_dir = os.path.join(self.base_path, "models", "sensevoice", "SenseVoiceSmall")
        self.fsmn_vad_dir = os.path.join(self.base_path, "models", "fsmn", "speech_fsmn_vad_zh-cn-16k-common-pytorch")
                
        # 创建信号发射器实例，用于线程间通信
        self.signal_emitter = SignalEmitter()
        
        # 初始化用户界面
        self.init_ui()
        
        # 初始化文件列表，用于存储待处理的音频文件路径
        self.file_list = []
        
        # 模型初始化标志和实例变量
        self.model_initialized = False  # 标记模型是否已初始化
        self.model = None  # 存储模型实例
        
    def init_ui(self):
        """初始化用户界面，设置窗口和各种控件"""
        # 设置窗口标题和大小
        self.setWindowTitle('SenseVoice 语音转文字工具')
        self.setGeometry(100, 100, 1000, 700)  # 设置窗口位置和大小
        
        # 设置窗口样式
        self.setContentsMargins(10, 10, 10, 10)
        
        # 创建标题栏
        title_layout = QHBoxLayout()
        title_layout.setContentsMargins(0, 0, 0, 0)
        
        # 添加标题标签
        title_label = QLabel('SenseVoice AI 语音转文字工具')
        title_label.setProperty("title", "true")  # 设置属性，以便在QSS中定位
        title_layout.addWidget(title_label)
        
        # 添加弹性空间，使按钮靠右对齐
        title_layout.addStretch()
        
        # 添加最小化按钮
        minimize_button = QPushButton('−')
        minimize_button.setFixedSize(30, 30)
        minimize_button.setProperty("action", "minimize")  # 设置属性
        minimize_button.clicked.connect(self.showMinimized)
        title_layout.addWidget(minimize_button)
        
        # 添加关闭按钮
        close_button = QPushButton('×')
        close_button.setFixedSize(30, 30)
        close_button.setProperty("action", "close")  # 设置属性
        close_button.clicked.connect(self.close)
        title_layout.addWidget(close_button)
        
        # 创建主布局（垂直布局）
        main_layout = QVBoxLayout()
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)
        
        # 添加标题栏到主布局
        main_layout.addLayout(title_layout)
        
        # 创建上传按钮 - 设置属性以匹配QSS
        upload_button = QPushButton('上传音频文件')
        upload_button.setMinimumHeight(45)
        upload_button.setProperty("action", "upload")  # 设置属性
        upload_button.clicked.connect(self.upload_files)
        main_layout.addWidget(upload_button)
        
        # 创建文件列表控件
        self.list_widget = QListWidget()
        file_list_label = QLabel('已上传文件:')
        main_layout.addWidget(file_list_label)
        main_layout.addWidget(self.list_widget)
        
        # 创建进度条控件
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        progress_label = QLabel('转换进度:')
        main_layout.addWidget(progress_label)
        main_layout.addWidget(self.progress_bar)
        
        # 创建开始转换按钮 - 设置属性以匹配QSS
        convert_button = QPushButton('开始转换')
        convert_button.setMinimumHeight(45)
        convert_button.setProperty("action", "convert")  # 设置属性
        convert_button.clicked.connect(self.start_conversion)
        main_layout.addWidget(convert_button)
        
        # 创建结果文本框
        self.result_text = QTextEdit()
        self.result_text.setReadOnly(True)
        result_label = QLabel('转换结果:')
        main_layout.addWidget(result_label)
        main_layout.addWidget(self.result_text)
        
        # 创建底部按钮布局
        bottom_layout = QHBoxLayout()
        
        # 创建清空按钮 - 设置属性以匹配QSS
        clear_button = QPushButton('清空')
        clear_button.setMinimumHeight(40)
        clear_button.setProperty("action", "clear")  # 设置属性
        clear_button.clicked.connect(self.clear_all)
        bottom_layout.addWidget(clear_button)
        
        # 创建保存按钮 - 设置属性以匹配QSS
        save_button = QPushButton('保存结果')
        save_button.setMinimumHeight(40)
        save_button.setProperty("action", "save")  # 设置属性
        save_button.clicked.connect(self.save_result)
        bottom_layout.addWidget(save_button)
        
        # 将底部布局添加到主布局
        main_layout.addLayout(bottom_layout)
        
        # 底部版权和感谢信息
        thanks_label = QLabel('感谢 SenseVoice 开源项目提供技术支持')
        thanks_label.setAlignment(Qt.AlignCenter)
        thanks_label.setProperty("thanks", "true")
        main_layout.addWidget(thanks_label)
        
        copyright_label = QLabel('© 2025 AI粉嫩特攻队. 版权所有.')
        copyright_label.setAlignment(Qt.AlignCenter)
        copyright_label.setProperty("copyright", "true")
        main_layout.addWidget(copyright_label)
        
        # 设置主窗口部件
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)
        
        # 连接信号到对应的槽函数
        self.signal_emitter.progress_signal.connect(self.update_progress)
        self.signal_emitter.result_signal.connect(self.show_result)
        self.signal_emitter.error_signal.connect(self.show_error)
        self.signal_emitter.info_signal.connect(self.show_info)
        self.signal_emitter.finished_signal.connect(self.conversion_finished)
        
    def upload_files(self):
        """处理文件上传功能，支持多文件选择"""
        # 创建文件对话框
        file_dialog = QFileDialog()
        file_dialog.setFileMode(QFileDialog.ExistingFiles)  # 设置为多文件选择模式
        file_dialog.setNameFilter("音频文件 (*.mp3 *.wav *.mp4 *.m4a *.flac)")  # 设置文件类型过滤器
        
        # 如果用户选择了文件并点击确定
        if file_dialog.exec_():
            filenames = file_dialog.selectedFiles()  # 获取选择的文件列表
            for filename in filenames:
                # 避免重复添加相同的文件
                if filename not in self.file_list:
                    self.file_list.append(filename)  # 添加到文件列表
                    self.list_widget.addItem(os.path.basename(filename))  # 在界面上显示文件名
    
    def initialize_model(self):
        """初始化语音识别模型，只在第一次使用时初始化"""
        if not self.model_initialized:
            try:
                # 检查模型文件夹是否存在
                if not os.path.exists(self.model_dir) or not os.path.exists(self.fsmn_vad_dir):
                    self.signal_emitter.error_signal.emit(f"模型文件不存在，请检查路径:\n{self.model_dir}\n{self.fsmn_vad_dir}")
                    return False
                
                # 调试信息
                self.signal_emitter.info_signal.emit(f"模型目录: {self.model_dir}")
                self.signal_emitter.info_signal.emit(f"VAD模型目录: {self.fsmn_vad_dir}")
                self.signal_emitter.info_signal.emit(f"远程代码路径: {os.path.join(self.base_path, 'model.py')}")
                self.signal_emitter.info_signal.emit(f"远程代码是否存在: {os.path.exists(os.path.join(self.base_path, 'model.py'))}")
                
                # 尝试导入FunASR
                try:
                    from funasr import AutoModel
                    self.signal_emitter.info_signal.emit("成功导入 AutoModel")
                except ImportError as ie:
                    self.signal_emitter.error_signal.emit(f"导入 AutoModel 失败: {str(ie)}")
                    return False
                
                # 尝试加载所有必要的模块
                try:
                    import funasr.auto.auto_model
                    import funasr.register
                    import funasr.models
                    import funasr.utils
                    self.signal_emitter.info_signal.emit("成功导入所有必要的FunASR模块")
                except ImportError as ie:
                    self.signal_emitter.error_signal.emit(f"导入FunASR模块失败: {str(ie)}")
                
                # 创建模型实例
                device = "cuda:0" if torch.cuda.is_available() else "cpu"
                # device = "cpu"
                try:
                    self.signal_emitter.info_signal.emit(f"使用设备: {device}")
                    
                    # 尝试加载模型
                    self.model = AutoModel(
                        model=self.model_dir,
                        trust_remote_code=True,
                        remote_code=os.path.join(self.base_path, "model.py"),
                        vad_model=self.fsmn_vad_dir,
                        vad_kwargs={"max_single_segment_time": 30000},
                        device=device,
                    )
                    self.model_initialized = True
                    self.signal_emitter.info_signal.emit("模型初始化成功!")
                    return True
                except TypeError as te:
                    self.signal_emitter.error_signal.emit(f"模型初始化类型错误: {str(te)}")
                    import traceback
                    error_detail = traceback.format_exc()
                    self.signal_emitter.error_signal.emit(f"错误详情:\n{error_detail}")
                    
                    # 尝试备用方案
                    self.signal_emitter.info_signal.emit("尝试备用初始化方法...")
                    try:
                        from pathlib import Path
                        model_path = Path(self.model_dir)
                        vad_path = Path(self.fsmn_vad_dir)
                        code_path = Path(os.path.join(self.base_path, "model.py"))
                        
                        import sys
                        if self.base_path not in sys.path:
                            sys.path.insert(0, self.base_path)
                            
                        from funasr.auto import AutoModel
                        
                        self.model = AutoModel.from_pretrained(
                            str(model_path),
                            trust_remote_code=True,
                            remote_code=str(code_path),
                            device=device,
                        )
                        self.model_initialized = True
                        self.signal_emitter.info_signal.emit("备用方法模型初始化成功!")
                        return True
                    except Exception as backup_err:
                        self.signal_emitter.error_signal.emit(f"备用方法也失败: {str(backup_err)}")
                    
                    return False
            except Exception as e:
                import traceback
                error_detail = traceback.format_exc()
                self.signal_emitter.error_signal.emit(f"模型初始化失败:\n{error_detail}")
                return False
        return True
    
    def start_conversion(self):
        """开始转换处理，在新线程中执行转换操作"""
        if not self.file_list:
            # 如果没有上传文件，发送错误信号
            self.signal_emitter.error_signal.emit("请先上传音频文件")
            return
        
        # 清空之前的结果
        self.result_text.clear()
        
        # 在新线程中执行转换，避免阻塞主线程
        threading.Thread(target=self.convert_files, daemon=True).start()
    
    def convert_files(self):
        """执行文件转换的主要逻辑，在单独的线程中运行"""
        # 添加路径调试信息
        self.signal_emitter.info_signal.emit(f"当前环境: {'打包环境' if getattr(sys, 'frozen', False) else '开发环境'}")
        self.signal_emitter.info_signal.emit(f"输出目录: {self.output_dir}")
        self.signal_emitter.info_signal.emit(f"基础路径: {self.base_path}")
        
        # 初始化模型
        if not self.initialize_model():
            return
        
        # 确保模型已正确初始化
        if self.model is None:
            self.signal_emitter.error_signal.emit("模型未能正确初始化")
            return
        
        total_files = len(self.file_list)
        for i, file_path in enumerate(self.file_list):
            try:
                # 更新进度条
                progress = int((i / total_files) * 100)
                self.signal_emitter.progress_signal.emit(progress)
                
                # 检查文件是否存在
                if not os.path.exists(file_path):
                    self.signal_emitter.error_signal.emit(f"文件不存在: {file_path}")
                    continue
                
                # 执行语音识别
                res = self.model.generate(
                    input=file_path,  # 输入文件路径
                    cache={},  # 缓存设置
                    language="auto",  # 自动检测语言
                    use_itn=True,  # 启用数字文本标准化
                    batch_size_s=60,  # 每批处理的音频长度（秒）
                    merge_vad=True,  # 合并VAD结果
                    merge_length_s=15,  # VAD合并长度阈值（秒）
                )
                
                # 对识别结果进行后处理
                text = rich_transcription_postprocess(res[0]["text"])
                
                # 发送结果信号
                file_name = os.path.basename(file_path)
                self.signal_emitter.result_signal.emit(file_name, text)
                
                # 保存结果到文件
                try:
                    output_file = os.path.join(self.output_dir, f"{os.path.splitext(file_name)[0]}.txt")
                    
                    # 检查文件路径是否有效
                    if not output_file or not os.path.isdir(os.path.dirname(output_file)):
                        raise IOError(f"无效的输出路径: {output_file}")
                    
                    # 将结果写入文件
                    with open(output_file, 'w', encoding='utf-8') as file:
                        if file is None:
                            raise IOError("无法创建输出文件")
                        file.write(text + '\n')
                    
                    # 添加明确的转换完成通知
                    self.signal_emitter.result_signal.emit(file_name, f"✅ 转换完成! 结果已保存到: {output_file}")
                except IOError as io_err:
                    self.signal_emitter.error_signal.emit(f"保存文件时出错: {str(io_err)}")
                except Exception as save_err:
                    self.signal_emitter.error_signal.emit(f"保存文件时出现未知错误: {str(save_err)}")
                
            except IOError as io_err:
                # 处理文件IO相关错误
                self.signal_emitter.error_signal.emit(f"处理文件 {os.path.basename(file_path)} 时出现IO错误: {str(io_err)}")
            except Exception as e:
                # 处理其他转换过程中的错误
                import traceback
                error_detail = traceback.format_exc()
                self.signal_emitter.error_signal.emit(f"处理文件 {os.path.basename(file_path)} 时出错: {str(e)}\n{error_detail}")
        
        # 完成所有转换
        self.signal_emitter.progress_signal.emit(100)  # 设置进度条为100%
        self.signal_emitter.finished_signal.emit()  # 发送完成信号
    
    def update_progress(self, value):
        """更新进度条显示"""
        self.progress_bar.setValue(value)
    
    def show_result(self, file_name, text):
        """在结果文本框中显示转换结果"""
        self.result_text.append(f"===== {file_name} =====\n")
        self.result_text.append(text)
        self.result_text.append("\n\n")
    
    def show_error(self, error_msg):
        """在结果文本框中显示错误信息"""
        self.result_text.append(f"错误: {error_msg}\n")
        
    def show_info(self, info_msg):
        """在结果文本框中显示错误信息"""
        self.result_text.append(f"info: {info_msg}\n")
    
    def conversion_finished(self):
        """处理转换完成后的操作"""
        self.result_text.append("==================================")
        self.result_text.append("🎉 所有文件转换完成！")
        self.result_text.append("==================================\n")
        
        # 如果有输出目录，显示它
        if hasattr(self, 'output_dir') and os.path.exists(self.output_dir):
            self.result_text.append(f"📁 输出目录: {self.output_dir}\n")
            
        QMessageBox.information(self, "转换完成", "所有文件已成功转换完成！")
    
    def clear_all(self):
        """清空所有数据和显示"""
        self.file_list = []  # 清空文件列表
        self.list_widget.clear()  # 清空文件列表显示
        self.result_text.clear()  # 清空结果显示
        self.progress_bar.setValue(0)  # 重置进度条
    
    def save_result(self):
        """保存转换结果到文件"""
        # 检查是否有结果可以保存
        if not self.result_text.toPlainText():
            self.show_error("没有可保存的结果")
            return
        
        # 创建保存文件对话框
        file_dialog = QFileDialog()
        file_dialog.setAcceptMode(QFileDialog.AcceptSave)  # 设置为保存模式
        file_dialog.setNameFilter("文本文件 (*.txt)")  # 设置文件类型过滤器
        file_dialog.setDefaultSuffix("txt")  # 设置默认文件后缀
        
        # 如果用户选择了保存位置并点击确定
        if file_dialog.exec_():
            filename = file_dialog.selectedFiles()[0]  # 获取选择的文件路径
            try:
                # 检查目录是否存在，如果不存在则创建
                save_dir = os.path.dirname(filename)
                if not os.path.exists(save_dir):
                    os.makedirs(save_dir, exist_ok=True)
                
                # 检查文件路径是否有效
                if not filename or not os.path.isdir(save_dir):
                    raise IOError(f"无效的保存路径: {filename}")
                
                # 将结果写入文件
                with open(filename, 'w', encoding='utf-8') as file:
                    if file is None:
                        raise IOError("无法创建输出文件")
                    file.write(self.result_text.toPlainText())  # 写入
                    
                self.show_result("保存成功", f"结果已保存到: {filename}")
            except IOError as e:
                self.show_error(f"保存文件时出错: {str(e)}")
            except Exception as e:
                import traceback
                error_detail = traceback.format_exc()
                self.show_error(f"保存文件时出现未知错误: {str(e)}\n{error_detail}")


if __name__ == '__main__':
    # 设置高DPI支持
    if hasattr(Qt, 'AA_EnableHighDpiScaling'):
        QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
    if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
        QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
    
    app = QApplication(sys.argv)
    
    # 设置应用程序图标 - 这将影响任务栏图标
    try:
        from PyQt5.QtGui import QIcon
        icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ai.ico")
        
        if os.path.exists(icon_path):
            app_icon = QIcon(icon_path)
            app.setWindowIcon(app_icon)  # 设置应用程序级别的图标
            print(f"应用程序图标已设置: {icon_path}")
        else:
            print(f"找不到图标文件: {icon_path}")
    except Exception as e:
        print(f"设置应用图标时出错: {str(e)}")
    
    # 加载应用程序字体
    QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fonts", "NotoSansSC-Regular.ttf"))
    QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fonts", "NotoSansSC-Bold.ttf"))
    
    # 设置全局字体
    font = QFont("Noto Sans SC", 10)
    app.setFont(font)
    
    # 显示启动画面
    splash = SplashScreen()
    splash.show()
    
    sys.exit(app.exec_())